Skip to main content

Caching

Caching reduces the number of requests that need to hit the server, saving bandwidth, CPU cycles, and database queries. This is essential for highly scalable APIs, especially when serving thousands or millions of clients.

There are two main caching types in HTTP REST APIs:

  1. Client-side caching – handled by the client/browser.
  2. Server-side caching – handled by proxies or the server itself.

HTTP caching uses headers to communicate caching rules between clients and servers.

ETag

An ETag is a unique identifier (usually a hash or version string) for a specific version of a resource. The server generates it, and the client can use it to check if the resource has changed.

How it works

  1. Client requests a resource:

    GET /api/users/123
  2. Server responds with the resource and an ETag header:

    HTTP/1.1 200 OK
    ETag: "abc123"
    Content-Type: application/json

    { "id": 123, "name": "Alice" }
  3. Next time, client asks if the resource has changed using If-None-Match:

    GET /api/users/123
    If-None-Match: "abc123"
  4. If resource hasn’t changed:

    HTTP/1.1 304 Not Modified
    • No body is sent.
    • Client can use cached version.

Benefits:

  • Saves bandwidth.
  • Avoids unnecessary data transfer.
  • Improves API scalability.

Last-Modified

Last-Modified is an HTTP header indicating the last time the resource was changed. Clients can use it to validate cache freshness.

How it works:

  1. Server responds with the Last-Modified header:

    HTTP/1.1 200 OK
    Last-Modified: Wed, 08 Jan 2026 10:00:00 GMT
  2. Client sends conditional request:

    GET /api/users/123
    If-Modified-Since: Wed, 08 Jan 2026 10:00:00 GMT
  3. Server checks:

    • If resource has not changed, respond 304 Not Modified.
    • If resource has changed, respond 200 OK with new content.

Benefits:

  • Simple to implement.
  • Works well when updates are time-based.

Cache-Control

Cache-Control gives explicit instructions on how long and under what conditions a response can be cached.

Common directives

DirectiveDescription
publicResponse can be cached by any cache (client, proxy).
privateResponse can only be cached by client, not proxy.
max-age=secondsMaximum time (in seconds) the response is considered fresh.
no-cacheClient must revalidate with server before using cached data.
no-storeNever cache this response.

Example in REST API:

GET /api/products
Response:
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: application/json

[
{ "id": 101, "name": "Laptop" },
{ "id": 102, "name": "Phone" }
]
  • Client or proxy can cache this response for 1 hour.
  • After 1 hour, it must check the server again.

Combination with ETag/Last-Modified:

  • You can combine Cache-Control with ETag or Last-Modified for conditional caching, which maximizes performance.

Practical Strategy for REST APIs

  1. Static resources (e.g., product catalog images)
    • Use Cache-Control: public, max-age=86400 (1 day)
    • Use ETag for updates.
  2. Dynamic resources (e.g., user profiles)
    • Use Cache-Control: private, max-age=60 (1 minute)
    • Use ETag or Last-Modified for conditional GETs.
  3. Sensitive resources (e.g., banking info)
    • Use Cache-Control: no-store to avoid caching.

Full REST API Response with All Headers

GET /api/users/123
Response:
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "a1b2c3"
Last-Modified: Wed, 08 Jan 2026 10:00:00 GMT
Cache-Control: private, max-age=300

{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}

Next request after 2 minutes:

GET /api/users/123
If-None-Match: "a1b2c3"
If-Modified-Since: Wed, 08 Jan 2026 10:00:00 GMT
  • If unchanged → 304 Not Modified → client uses cached data.
  • If changed → 200 OK with new data → update cache.